Migrating Azure Functions from .NET 6 in-process to .NET 7 isolated

2023-10-13

Isolated workers - the future of Azure Functions

The Azure Functions team has indicated that net8 will be the last LTS release to receive in-process model support in Azure Functions.

Coupled with the desire to take advantage of the UInt128 support that arrived with .NET 7, I decided it was time to upgrade the functions supporting DevSecTools from .NET 6 in-process to .NET 7 isolated.

Gotchas

Microsoft has a pretty good migration guide that covers what’s going to change in your app, but I ran into a few frustrations along the way.

Testability

Proper testing support for Azure Functions has been a long time coming and is worse in the isolated model due to use of extension methods that make use of internal interfaces which are non-trivial to intercept/mock. Fortunately the Functions Team have enabled support for Castle.DynamicProxy for those willing to take a gamble on the stability of the library internals.

Azure-side changes

As documented, you’ll need update FUNCTIONS_WORKER_RUNTIME from dotnet to dotnet-isolated.

Be careful of slots! You don’t want an old value for FUNCTIONS_WORKER_RUNTIME being dropped in because of a slot-swap

There’s an additional (albeit poorly documented) step involved when moving from net6 to net7 - the linuxFxVersion needs to be set. If you fail to do so your logs will indicate that the appropriate version of the .NET is not installed, for example:

1
2
3
4
5
6
7
8
9
10
11
12
      [Error]   Failed to start a new language worker for runtime: dotnet-isolated.
[Information] You must install or update .NET to run this application.
[Information] App: /home/site/wwwroot/DevSecTools.Api.dll
[Information] Architecture: x64
[Information] Framework: 'Microsoft.NETCore.App', version '7.0.0' (x64)
[Information] .NET location: /usr/share/dotnet/
[Information] The following frameworks were found:
[Information] 6.0.21 at [/usr/share/dotnet/shared/Microsoft.NETCore.App]
[Information] Learn about framework resolution:
[Error] https://aka.ms/dotnet/app-launch-failed
[Information] To install missing framework, download:
[Information] https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=7.0.0&arch=x64&rid=debian.11-x64

To remedy, update the linuxFxVersion with the az cli:

1
az functionapp config set --name <FUNCTION_APP> --resource-group <RESOURCE_GROUP> --linux-fx-version "DOTNET-ISOLATED|7.0"

The linuxFxVersion needs to be updated on all of your slots (there is a --slot argument supported by the az cli). I also found it necessary to manually restart the function app.

Managed identity flakiness

I unexpectedly saw problems attempting to authenticate via Managed Identity, logs were somewhat vague suggesting some sort of connectivity problem.

1
2
3
4
5
6
Azure.Identity.CredentialUnavailableException: ManagedIdentityCredential authentication unavailable. Multiple attempts failed to obtain a token from the managed identity endpoint.
---> System.AggregateException: Retry failed after 4 tries. Retry settings can be adjusted in ClientOptions.Retry or by configuring a custom retry policy in ClientOptions.RetryPolicy.
(The operation was cancelled because it exceeded the configured timeout of 0:00:01. Network timeout can be adjusted in ClientOptions.Retry.NetworkTimeout.)
(The operation was cancelled because it exceeded the configured timeout of 0:00:01. Network timeout can be adjusted in ClientOptions.Retry.NetworkTimeout.)
(The operation was cancelled because it exceeded the configured timeout of 0:00:01. Network timeout can be adjusted in ClientOptions.Retry.NetworkTimeout.)
(The operation was cancelled because it exceeded the configured timeout of 0:00:01. Network timeout can be adjusted in ClientOptions.Retry.NetworkTimeout.)

Ultimately I resolved this by disabling and re-enabling managed identity. This generates a new objectId, which requires re-mapping all the appropriate permissions/roles.


Comments: